﻿using PSL.Collection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PSL.Lexer
{
    public class TokenizerHelper
    {
        #region [GetStringUntilChars]
        /// <summary>
        /// Get string until end-symbol, like space or linebreak
        /// </summary>
        /// <param name="Line">Code-Line</param>
        /// <param name="EndSymbol">Symbil which break up parsing</param>
        /// <returns>String</returns>
        public static string GetStringUntilChars(string Line, params string[] EndSymbol)
        {
            string[] splittedString = Line.Split(EndSymbol, StringSplitOptions.RemoveEmptyEntries);

            if (splittedString.Length > 0)
            {
                return splittedString.OrderBy(Item => Item.Length).First();
            }
            else
            {
                return "";
            }
        }
        #endregion

        #region [RemoveFromLineStart]
        /// <summary>
        /// Remove a string from the Line-Start
        /// </summary>
        /// <param name="Line"></param>
        /// <param name="ToRemove"></param>
        /// <returns></returns>
        public static string RemoveFromLineStart(string Line, string ToRemove)
        {
            if (ToRemove.Length <= Line.Length)
            {
                return Line.Substring(ToRemove.Length, (Line.Length - ToRemove.Length));
            }
            else
            {
                return "";
            }
        }
        #endregion

        #region [GetStrintBetweenChars]
        /// <summary>
        /// Get string between two chars
        /// </summary>
        /// <param name="Line">Code-Line</param>
        /// <param name="Start">Start-String</param>
        /// <param name="End">End-String</param>
        /// <returns></returns>
        public static string GetStrintBetweenChars(string Line, char Start, char End)
        {
            string returnValue = "";

            if (Line.IndexOf(Start) < Line.IndexOf(End) && Line.IndexOf(Start) > -1)
            {
                returnValue = Line.Substring(Line.IndexOf(Start) + 1, Line.IndexOf(End) - (Line.IndexOf(Start) + 1));
            }
            else
            {
                throw new Exceptions.SyntaxError(String.Format("Syntax-Error: Could not find '{0}' and '{1}' in the line: {2}", Start, End, Line));
            }

            return returnValue;
        }
        #endregion

        #region [IsDigit]
        /// <summary>
        /// Proofs, if a char is a digit
        /// </summary>
        /// <param name="Input">True if the char is an digit</param>
        /// <returns>Is-Digit</returns>
        public static bool IsDigit(char Input)
        {
            short digit;
            return short.TryParse(Input.ToString(), out digit);
        }
        #endregion

        #region [GetNextDelegate]
        /// <summary>
        /// Get the content of a delegate-string
        /// </summary>
        /// <param name="Input"></param>
        /// <returns></returns>
        public static string GetNextDelegate(string Input)
        {
            string returnValue = "";
            int cCountOpenedDelegates = 0;

            for (int i = 0; i < Input.Length; i++)
            {
                if (Input.Length >= i + 1)
                {
                    string cPair = Input.Substring(i, 1);
                    if (cPair == "{")
                    {
                        if (i == 0)
                        {
                            cCountOpenedDelegates++;
                        }
                        else if (Input[i - 1] != '\\')
                        {
                            cCountOpenedDelegates++;
                        }
                    }
                    else if (cPair == "}")
                    {
                        if (i == 0)
                        {
                            cCountOpenedDelegates--;
                        }
                        else if (Input[i - 1] != '\\')
                        {
                            cCountOpenedDelegates--;
                        }
                    }
                }

                if (cCountOpenedDelegates == 0)
                {
                    returnValue = Input.Substring(1, i - 1);
                    break;
                }
            }

            if (cCountOpenedDelegates > 0)
            {
                throw new Exceptions.SyntaxError("Syntax-Error: Missing } in Line: " + Input);
            }

            return returnValue;
        }
        #endregion

        #region [GetNextQuotedString]
        /// <summary>
        /// Get the next completely quoted string
        /// </summary>
        /// <param name="Input">Input-String</param>
        /// <returns>Object witl all information</returns>
        public static QuotedParameterParserResult GetNextQuotedString(string Input)
        {
            // Define return-value / vars
            QuotedParameterParserResult returnValue = new QuotedParameterParserResult();
            returnValue.RemovedChars = 0;
            returnValue.Result = "";

            bool addNextDirect = false;
            int unescapedQuotes = 0;

            for (int i = 0; i < Input.Length; i++)
            {
                if (addNextDirect)
                {
                    returnValue.Result += Input[i];

                    addNextDirect = false;
                    continue;
                }

                if (Input[i] == '"')
                {
                    unescapedQuotes++;
                }

                if (Input[i] == '\\')
                {
                    // This char will not be added to the result
                    returnValue.RemovedChars++;

                    addNextDirect = true;
                    continue;
                }
                returnValue.Result += Input[i];

                // Leave if all unescaped quoates are closed
                if (unescapedQuotes == 2)
                {
                    returnValue.Result = returnValue.Result.Substring(1, (i - returnValue.RemovedChars) - 1);
                    returnValue.RemovedChars += 2;
                    break;
                }
            }

            if (unescapedQuotes % 2 != 0)
            {
                throw new Exceptions.SyntaxError("Syntax-Error: Not all double-quotes are closed");
            }

            return returnValue;
        }
        #endregion

        #region [Gell all token until a specific char]
        /// <summary>
        /// Return a Dequeue of tokens until a specific char
        /// </summary>
        /// <param name="Tokens"></param>
        /// <param name="Char"></param>
        /// <returns></returns>
        public static Dequeue<string> GetTokensUntilChar(Dequeue<string> Tokens, char Char = ';')
        {
            Dequeue<string> returnQueue = new Dequeue<string>();

            while (Tokens.Count > 0)
            {
                string token = Tokens.PeekFirst();

                if (token[0] != Char)
                {
                    returnQueue.PushBack(Tokens.PopFirst());
                }
                else
                {
                    break;
                }
            }

            return returnQueue;
        }
        #endregion

        public static QuotedParameterParserResult GetNextComplexString(string Input, char StartEndChar)
        {
            // Define return-value / vars
            QuotedParameterParserResult returnValue = new QuotedParameterParserResult();
            returnValue.RemovedChars = 0;
            returnValue.Result = "";

            bool addNextDirect = false;
            int unescapedQuotes = 0;

            for (int i = 0; i < Input.Length; i++)
            {
                if (addNextDirect)
                {
                    returnValue.Result += Input[i];

                    addNextDirect = false;
                    continue;
                }

                if (Input[i] == StartEndChar)
                {
                    unescapedQuotes++;
                }

                if (Input[i] == '\\')
                {
                    // This char will not be added to the result
                    returnValue.RemovedChars++;

                    addNextDirect = true;
                    continue;
                }
                returnValue.Result += Input[i];

                // Leave if all unescaped quoates are closed
                if (unescapedQuotes == 2)
                {
                    returnValue.Result = returnValue.Result.Substring(0, (i - returnValue.RemovedChars) + 1);
                    break;
                }
            }

            if (unescapedQuotes % 2 != 0)
            {
                throw new Exceptions.SyntaxError("Syntax-Error: Not all " + StartEndChar + " are closed");
            }

            return returnValue;
        }

        #region [ProofObjectName]
        /// <summary>
        /// Proof, if a function or parametername is correct
        /// </summary>
        /// <param name="Name">Name of the function or parameter</param>
        /// <param name="ProofObjectType">Type which will be proofed</param>
        /// <returns>True if the name is correct</returns>
        public static bool ProofObjectName(string Name, ProofObjectType ProofObjectType, bool ThrowException = true)
        {
            bool returnValue = true;

            #region [Proof Function-Name]
            for (int i = 0; i < Name.Length; i++)
            {
                bool throwSyntaxError = false;

                if (ParserConstants.FunctionParameterChars.Contains(Name[i]))
                {
                    if (i == 0)
                    {
                        if (ParserConstants.NotBeginParameterChars.Contains(Name[i]))
                        {
                            throwSyntaxError = true;
                        }
                    }
                }
                else
                {
                    throwSyntaxError = true;
                }

                if (throwSyntaxError && ThrowException)
                {
                    if (ProofObjectType == PSL.ProofObjectType.FunctionAndVariable)
                    {
                        throw new Exceptions.SyntaxError(String.Format("Syntax-Error: Near:" + Name[i] + "; Function or variable: {0}", Name));
                    }
                    else if (ProofObjectType == PSL.ProofObjectType.Parameter)
                    {
                        throw new Exceptions.SyntaxError(String.Format("Syntax-Error: Near:" + Name[i] + "; Parameter: {0}", Name));
                    }
                    else if (ProofObjectType == PSL.ProofObjectType.UnquotedString)
                    {
                        throw new Exceptions.SyntaxError(String.Format("Syntax-Error: Near:" + Name[i] + "; in unquited string: {0}", Name));
                    }
                }
                else if (throwSyntaxError && !ThrowException)
                {
                    returnValue = false;
                    break;
                }
            }

            return returnValue;
            #endregion
        }
        #endregion

        #region [HandleParameterMissingException]
        /// <summary>
        /// Handle Missing-Parameter exception
        /// </summary>
        /// <param name="FunctionName">Name of the function</param>
        /// <param name="ParameterName">Name of the parameter</param>
        public static void HandleParameterMissingException(string FunctionName, string ParameterName)
        {
            throw new Exceptions.MissingParameterException(String.Format("Missing the parameter \"{0}\" for the function: \"{1}\"", ParameterName, FunctionName));
        }
        #endregion

        #region [HandleInvalidParameterException]
        /// <summary>
        /// Handle Parameter-Not-Found exception
        /// </summary>
        /// <param name="FunctionName">Name of the function</param>
        /// <param name="ParameterName">Name of the parameter</param>
        public static void HandleInvalidParameterException(string FunctionName, string ParameterName)
        {
            throw new Exceptions.InvalidParameterException(String.Format("Parameter \"{0}\" is invalid for the function: \"{1}\"", ParameterName, FunctionName));
        }
        #endregion
    }
}
